#Covered by GPL V2.0
#Coded by Carlos del Ojo Elias (deepbit@gmail.com)

#evolution of wfuzz by Carlos del Ojo and Christian Martorella

from __future__ import generators

import copy
from reqresp import *
import binascii
import sys
import md5
import threading
import getopt
from time import sleep
import os
from encoders import *
from payloads import *
from dictio import dictionary
import re
from urlparse import urlparse
import datetime

ENCODERS={}
encs=dir(encoders)
for i in encs:
	try:
		if i[:8]=='encoder_':
			ENCODERS[getattr(encoders,i).text.lower()]=i
	except:
		pass

class DiccPermutator:
	def __init__(self,dicts):
		self.vars=[]
		self.dicts=[]

		for var,dic in dicts.items():
			self.vars.append(var)
			self.dicts.append(dic)

	def __iter__(self):
		self.generator=self.permut2(self.dicts,[])
		return self

	def __len__(self):
		a=1
		for i in self.dicts:
			a=a*len(i)

		return a

	def next(self):
			return self.generator.next()

	#### Da iwal el diciconario que pille tan solo se usa para pathFuzzing ( q es @PFZZ )
	def getFake(self):
		for i in self.dicts:
			return i.getPrepend()
		return None

	def permut2(self,dicts,cur):
		if not dicts:
			res={}
			for i in xrange(len(self.vars)):
				res[self.vars[i]]=cur[i]
			yield res
			return
	
		a,b=dicts[0],dicts[1:]
		for i in a:
			for x in self.permut2(b,cur+[i]):
				yield x


# Generate_fuzz evolution2.0
class requestGenerator:
	def __init__(self,rawreq,dicts,proxy=None,FollowLocation=False,Auth=[None,None],schema="http"):
		self.rawReq=rawreq
		self.schema=schema

		tmp=[]
		for i in dicts:
			if not i in rawreq and not i in Auth[1]:
				tmp.append(i)

		for i in tmp:
			del dicts[i]

		self.dictPermutator=DiccPermutator(dicts)
		self.proxy=proxy
		self.fLocation=FollowLocation
		self.authMethod=Auth[0]
		self.auth=Auth[1]

	def __len__(self):
		return len(self.dictPermutator)
	
	def __iter__(self):	
		self.dictPermutator.__iter__()
		return self

	def getFake(self):
		fakePattern=self.dictPermutator.getFake()
		if fakePattern and fakePattern[-1]=="/":
			fakePattern+="DeepbitIsGod"
			fake=Request()
			fake.parseRequest(self.rawReq.replace("@PFZZ",fakePattern),self.schema)

			if self.auth:
				fake.setAuth(self.authMethod,self.auth)

			fake.setFollowLocation(self.fLocation)
			fake.setProxy(self.proxy)

			return fake
		return None

	def next(self):
		replaces=self.dictPermutator.next()
		raw=self.rawReq
		userpw=self.auth
		for i,j in replaces.items():
			raw=raw.replace(i,j)
			if userpw:
				userpw=userpw.replace(i,j)

		a=Request()
		a.parseRequest(raw,self.schema)

		if userpw:
			a.setAuth(self.authMethod,userpw)

		a.description=str(replaces)

		a.setFollowLocation(self.fLocation)
		a.setProxy(self.proxy)

		return a


class FuzzResult:
	def __init__(self,request,saveMemory=True):

		global OS

		self.completeUrl=request.completeUrl
		self.contenttype=""

		self.number=0

		#######################################
		self.len=0
		self.lines=0
		self.words=0
		self.code=0
		self.md5=""
		self.location=None
		self.descrip=request.description
		
		### Used only when saveMemory = False
		self.respHeaders=""
		self.html=""
		########################################
	
		request.setConnTimeout(20)
		request.setTotalTimeout(20)
		i=2
		x=True
		while i:
			try:
				request.perform()
				break
			except Exception,a:
				i-=1
				x=False
		if not i:
			return 

		self.len=len(request.response.getAll())
		self.lines=request.response.getAll().count("\n")
		self.words=len(re.findall("\S+",request.response.getAll()))
		self.code=int(request.response.code)

		if self.code==404:
			return

		if not saveMemory:
			self.respHeaders=request.response.getTextHeaders()
			self.html=request.response.getContent()

		self.location=request.response.getLocation()
		self.completeUrl=request.finalUrl
		self.contenttype=request.response["Content-Type"]


		m=md5.new()
		m.update(request.response.getAll())

		self.md5=m.hexdigest()

	def getResponse(self):	
		return self.respHeaders,self.html

	def __str__(self):
		return ("%05d:  C=%03d   %4d L\t   %5d W\t  %5d Ch\t \"%s\"" % (self.number,self.code,self.lines,self.words,self.len,self.descrip)) 

	def setNumber(self,n):
		self.number=n


	def __getitem__ (self,key):
		for i,j in self.req.response.getHeaders():
			if key==i:
				return  j
		print "Error al obtener header!!!"




#####################################################################################################
#####################################################################################################
#####################################################################################################


class Fuzzer:
	def __init__(self,req,dicts,proxy=None,FollowLocation=False,saveMemory=True,threads=15,Auth=[None,None]):
		self.schema=req.schema
		self.genReq=requestGenerator(req.getAll(),dicts,proxy,FollowLocation,Auth,schema=self.schema)

		self.results=[]
		self.threads=threads
		self.run=True
		self.threads_list=[]
		self.nres=0
		self.Semaphore=threading.BoundedSemaphore(value=self.threads)
		self.Semaphore_Mutex=threading.BoundedSemaphore(value=1)

		self.saveMemory=saveMemory
		self.number=0

		self.totalFuzzs=len(self.genReq)

		self.state="Configured"
		self.initime=None


	def __len__ (self):
		return len(self.genReq)


	def Launch(self):
		self.initime=datetime.datetime.today()
		self.state="Running"
		self.Handler=threading.Thread(target=self.threadHandler, kwargs={})
		self.Handler.start()
	
	def wait(self):
		self.Handler.join()

	def preLaunch(self):
		pass


	def threadHandler(self):

		while self.run:
			self.preLaunch()

			for i in self.genReq:
				if not self.run:
					break

				while not self.Semaphore.acquire(blocking=False):
					sleep(.1)
					tl=[]
					for thr in self.threads_list:
						if not thr.isAlive():
							thr.join()
							tl.append(thr)
					for thr in tl:
						self.threads_list.remove(thr)
											
				th=threading.Thread(target=self.attack, kwargs={"req":i})
				th.start()
				self.threads_list.append(th)

			tl=[]
			for thr in self.threads_list:
				thr.join()
				tl.append(thr)
			for thr in tl:
				self.threads_list.remove(thr)

			self.follow()

		if self.nres==self.totalFuzzs:
			self.state="Finished"
		else:
			self.state="Interrupted"

	def follow(self):
		self.run=False


	def attack (self,req):
		try :
			res=FuzzResult(req,self.saveMemory)

			self.Semaphore_Mutex.acquire()

			self.number+=1
			res.setNumber(self.number)
			self.Semaphore_Mutex.release()

			self.agregaresultado(res)

		except Exception,a:
			print "Error in attack(): "+str(a)
	
		self.Semaphore.release()

	def agregaresultado (self,res):
		self.Semaphore_Mutex.acquire()
		self.results.append(res)
		self.nres+=1
		self.Semaphore_Mutex.release()

	def next(self):
		while self.running() or self.results:
			if self.results:
				self.Semaphore_Mutex.acquire()
				a=self.results.pop(0)
				self.Semaphore_Mutex.release()
				return a
			sleep(.05)
		raise StopIteration

	def getAvailableResults(self):
		self.Semaphore_Mutex.acquire()
		a=self.results
		self.results=[]
		self.Semaphore_Mutex.release()
		return a
		

	def __iter__(self):
		return self

 	def numResults (self):
		nres=len(self.results)
		return self.nres
	
	def running(self):
		return self.Handler.isAlive() or self.run

	def stop(self):
		self.state="Stopping"
		self.run=False
		for i in self.threads_list:
			i.join()

	def status(self):
		now=datetime.datetime.today()
		delta=now-self.initime
		if self.nres>0:
			delta=(delta.seconds*(self.totalFuzzs-self.nres))/self.nres
		else:
			delta=0

		mins=delta/60
		secs=delta%60
		if mins>0:
			mins="%d m " % (mins)
		else:
			mins=""
		
		secs="%d s" % (secs)

		eta=mins+secs

		if self.nres==self.totalFuzzs:
			eta="Done"

		return [self.nres,self.totalFuzzs,eta]

class PathFuzzer(Fuzzer):
	def __init__(self,req,dictio,maxLevel=3,proxy=None,saveMemory=True,threads=15,Auth=[None,None],extensions=[]):
		url=req.completeUrl+"@PFZZ"
		req.setUrl(url)
		dictio={"@PFZZ":dictio}
		self.maxLevel=maxLevel

		if extensions:
			extensions=["."+i for i in extensions if i]
			if not "" in extensions:
				extensions.append("")
			dictio["@PFZZ"].getPayload().append(payload_list(extensions))

		if self.maxLevel==1:
			fl=True
		else:
			fl=False

		Fuzzer.__init__(self,req,dictio,proxy,fl,saveMemory,threads,Auth)
		self.level=0


		self.request=req.getAll()

		self.payload=dictio["@PFZZ"].getPayload()
		self.encoder=dictio["@PFZZ"].getEncoder()
		self.recursDictios=[]


		self.proxy=proxy
		self.Auth=Auth

		self.banned=None


	def preLaunch(self):
		self.level+=1
		a=self.genReq.getFake()
		if a:
			fz=FuzzResult(a)
			if fz.code>=200 and fz.code<400:
				self.banned=[fz.code,fz.lines]
			else:
				self.banned=None



	def agregaresultado (self,res):
		self.Semaphore_Mutex.acquire()
		self.results.append(res)
		dicc=eval(res.descrip)
		refuzz=dicc["@PFZZ"]

 #		if self.banned and res.code==self.banned[0] and res.lines==self.banned[1]:
 #			pass
 #		else:
		if self.level==self.maxLevel-1:
			fl=False
		else:
			fl=True

		if res.code>=200 and res.code<300:
			dic=self.getBackupDicc(refuzz)
			if dic:
				rq=requestGenerator(self.request,dic,self.proxy,fl,self.Auth,schema=self.schema)	
				self.totalFuzzs+=len(rq)
				self.recursDictios.append(rq)

		if res.code>=300 and res.code<400:
			loc=res.location
			dict=self.getRedirectDicc(refuzz,loc)
			if loc and dict:
				rq1=requestGenerator(self.request,dict,self.proxy,fl,self.Auth,schema=self.schema)
				rq2=requestGenerator(self.request,self.getCompressDicc(refuzz),self.proxy,fl,self.Auth,schema=self.schema)
				self.totalFuzzs+=len(rq1)
				self.totalFuzzs+=len(rq2)
				self.recursDictios.append(rq1)
				self.recursDictios.append(rq2)


		self.nres+=1
		self.Semaphore_Mutex.release()

	def getRedirectDicc(self,refuzz,loc):
		if re.findall (refuzz+'/$',loc):
			newdict=dictionary()
			newdict.setPayload(self.payload)
			newdict.setEncoder(self.encoder)
			newdict.setPrepend(refuzz+"/")
			if self.level<self.maxLevel:
				return {"@PFZZ":newdict}

		return None
		

	def getBackupDicc(self,refuzz):
		parts=refuzz.split(".")
		if parts[-1] in ['php','jsp','asp','aspx','pl','cgi','do']:
			refuzz=parts[0]
			ext=".".join(parts[1:])
			if ext:
				ext="."+ext

			newdict=dictionary()
			newdict.setPayload(payload_list([".bak",ext+"~",".BAK",".old",".rej",".orig",".inc"]))
			newdict.setEncoder(self.encoder)
			newdict.setPrepend(refuzz)

			return {"@PFZZ":newdict}
			
		return None

	def getCompressDicc(self,refuzz):
		list=refuzz.split(".")
		refuzz=list[0]
		ext=".".join(list[1:])
		if ext:
			ext="."+ext

		newdict=dictionary()
		newdict.setPayload(payload_list([".zip",".tar",".tar.gz",".rar",".tgz"]))
		newdict.setEncoder(self.encoder)
		newdict.setPrepend(refuzz)

		return {"@PFZZ":newdict}
		

	def follow(self):
		if not self.recursDictios:
			self.run=False
		else:
			self.genReq=self.recursDictios.pop()
	#		self.threadHandler()

		self.level-=1





		
#############################################################################################################
#############################################################################################################
#################		 INTERFACE CONOSLA											  ####################
#############################################################################################################
#############################################################################################################


def select_encoding(typ):
	typ=typ.lower()

	if not typ in ENCODERS:
		print typ+" encoding does not exists (-e help for a list of available encodings)" 
		sys.exit(-1)

	return getattr (encoders,ENCODERS[typ])()


if __name__=="__main__":

	banner='''
-------------------------------------
@ DwebF  1.0                        @
@ Coded by:                         @\\
@ Carlos del ojo                    @
@   - deepbit@gmail.com             @\\
-------------------------------------
'''
	usage='''
Usage: %s [options] <url>\r\n
Options:

Global Parameters
-----------------

	--basic auth  : in format "user:pass" or "admin:@1" ;)
	--ntlm auth   : in format "domain\user:pass" or "domain\@1:@2" ;)
	--digest auth : in format "user:pass" or "@FUZZ:@1" ;)
	
	--hc N[,N]+ : Hide requests with the specified[s] code
	--hl N[,N]+ : Hide responses with the specified[s] number of lines
	--hw N[,N]+ : Hide responses with the specified[s] number of words
	
	-x addr     : use Proxy (ip:port)
	-d postdata : Use post data (ex: "id=FUZZ&catalogue=1")
	-b cookie   : Specify a cookie for the requests
	-t threads  : Specify number of threads. Default(15)
	-H header   : Specify any HTTP header. Allowed multiple times

PathFuzz Parameters
-------------------

	-P payload [-r recursion_level] [-e extension1,extension2,extension3...] URL
		default recursion level: 3

Example:	
wfuzz.py -P file,wordllits/common.txt,md5 -r 3 http://www.site.com/
wfuzz.py -P range,1-10 -e .php,.js,.jsp http://www.site.com/


Generic Fuzz Parameters
-----------------------

	-p payload  : Define a payload. 
                  You can define multiple payloads with multiples '-p'

	              Payload format: ID,type,opts         
	              ID can be any string 
	
	              Examples: @1,file,wordllits/common.txt[,encoding-type]
	                        @OTHERID,range,1-3[,encoding-type]
	                        @FUZZ,hexrange,1-FF[,encoding-type]
	
	--follow	: Follow if exists 'Location' header
	
	
EXAMPLES:
wfuzz.py -p ANYID,file,wordlists/common.txt http://www.edge-security.com/ANID
wfuzz.py -p @1,file,a.txt -p @2,range,1-10 http://www.edge-security.com/@1_@2

Available encodings:

''' % (sys.argv[0])

	for i in ENCODERS:
		usage+=" - "+i+"\r\n"

	FollowLocation=False
	PATHFUZZ=False
	RecursLevel=3

	try:
		opts, args = getopt.getopt(sys.argv[1:], "e:r:P:p:x:b:d:t:w:V:H:",['hc=','hl=','hw=','ntlm=','basic=','digest=','follow'])
		optsd={}
		for i,j in opts:
			if not i in optsd:
				optsd[i]=[]
			optsd[i].append(j)

		if not "-t" in optsd:
			optsd["-t"]=[15]

		if not "-x" in optsd:
			optsd["-x"]=[None]

		if "--follow" in optsd:
			FollowLocation=True

		url=args[0]

	except Exception,qw: 
		print 
		print banner
		print usage
		sys.exit(-1)
	
	dicts={}
	if "-p" in optsd:
		for i in optsd["-p"]:
			try:
				encoding=None
				vals=i.split(",")
				id,type,par=vals[:3]
				if type=="file":
					p=payload_file(par)	
				elif type=="range":
					p=payload_range(par)	
				elif type=="hexrange":
					p=payload_hexrange(par)	
	
				if len(vals)==4:
					encoding=vals[3]
	
				dic=dictionary()
				dic.setPayload(p)
				if encoding:
					dic.setEncoder(select_encoding(encoding))
	
				dicts[id]=dic
	
			except Exception,a:
				print "Error in '-p %s': %s" % (i,a)
				sys.exit(-1)
	
	Auth=[None,None]
	if "--basic" in optsd:
		Auth=["basic",optsd["--basic"][0]]
	if "--digest" in optsd:
		Auth=["digest",optsd["--digest"][0]]
	if "--ntlm" in optsd:
		Auth=["ntlm",optsd["--ntlm"][0]]

	hidecodes=[]
	if "--hc" in optsd:
		hidecodes=[int(i) for i in optsd["--hc"][0].split(",")]
	hidelines=[]
	if "--hl" in optsd:
		hidelines=[int(i) for i in optsd["--hl"][0].split(",")]
	hidewords=[]
	if "--hw" in optsd:
		hidewords=[int(i) for i in optsd["--hw"][0].split(",")]

	if "-P" in optsd:
		PATHFUZZ=True
		if "-p" in optsd:
			print "You can't use -P and -p at the same time!!!!"
			sys.exit(-1)

		i=optsd["-P"][0]
		try:
			encoding=None
			vals=i.split(",")
			type,par=vals[:2]
			if type=="file":
				p=payload_file(par)	
			elif type=="range":
				p=payload_range(par)	
			elif type=="hexrange":
				p=payload_hexrange(par)	

			if len(vals)==3:
				encoding=vals[2]

			dic=dictionary()
			dic.setPayload(p)
			if encoding:
				dic.setEncoder(select_encoding(encoding))

		except Exception,a:
			print "Error in '-P %s': %s" % (i,a)
			sys.exit(-1)



	rq=Request()
	rq.setUrl(url)

	if "-b" in optsd:
		rq.addHeader("Cookie",optsd["-b"][0])
	
	if "-H" in optsd:
		for i in optsd["-H"]:
			j,k=i.split(":")
			j=j.strip()
			k=k.strip()
			rq.addHeader(j,k)

	if "-d" in optsd:
		rq.setPostData(optsd["-d"][0])

	if "-r" in optsd:
		RecursLevel=int(optsd["-r"][0])
	exts=[]
	if "-e" in optsd:
		exts=optsd["-e"][0].split(",")


	if not PATHFUZZ:
		ob=Fuzzer(rq,dicts,proxy=optsd["-x"][0],threads=int(optsd["-t"][0]),FollowLocation=FollowLocation,Auth=Auth)
	else:
		ob=PathFuzzer(rq,dic,maxLevel=RecursLevel,proxy=optsd["-x"][0],threads=int(optsd["-t"][0]),Auth=Auth,extensions=exts)

	ob.Launch()

	actual=0

	print banner

	print "---------------------------------------------------------------------------------"
	print "#FZ     Code     #Lines      #Words       #Bytes         Payloads    "
	print "_________________________________________________________________________________\r\n"


	try:
		lastdec=0
		for i in ob:
			if i.code not in hidecodes and i.lines not in hidelines and i.words not in hidewords:
				sys.stdout.write("\r\x1b[0K"+str(i))	
				sys.stdout.flush()
				print
			else:
				sys.stdout.write("\r\x1b[0K"+str(i))
				sys.stdout.flush()
	
			time=datetime.time()
			dec=int(datetime.datetime.now().minute)
			if dec!=lastdec:
				lastdec=dec
				print "\r\x1b[0K"+str(ob.status())

				
		print "\r\x1b[0K"

	except KeyboardInterrupt:
		print "Interrupt!!!!"
		print "Stopping... please wait"
		ob.stop()
		ob.wait()
		print "Bye!"
		sys.exit(-1)
	except Exception,a:
		print "Error!: ",a
		sys.exit(-1)
		

